import 'package:flutter/material.dart';
import 'dart:math';

class TouchDemo extends MaterialPageRoute<Null> {
    TouchDemo()
        : super(builder: (BuildContext ctx) {
        return PageContent();
    });
}

class PageContent extends StatefulWidget {
    @override
    State<StatefulWidget> createState() {
        return PageState();
    }
}

class PageState extends State<PageContent> with SingleTickerProviderStateMixin {
    TabController controller;
    
    @override
    void initState() {
        super.initState();
        controller = TabController(length: 2, vsync: this);
    }
    
    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text("触摸事件demo"),
                bottom: TabBar(
                    tabs: [
                        Tab(
                            text: "demo1",
                        ),
                        Tab(
                            text: "demo2",
                        ),
                    ],
                    controller: controller,
                ),
            ),
            body: TabBarView(
                children: <Widget>[
                    TouchDemo1(),
                    TouchDemo2(),
                ],
                controller: controller,
            ),
        );
    }
}

class TouchDemo1 extends StatefulWidget {
    final double boxPosition;
    
    TouchDemo1({
        this.boxPosition = 0.0,
    });
    
    @override
    State<StatefulWidget> createState() {
        return AnDemo3State();
    }
}

class AnDemo3State extends State<TouchDemo1> with TickerProviderStateMixin {
    //因为这里的ticker要反复执行
    
    double boxPos;
    double boxPosOnStart;
    Offset start;
    Offset point;

//    AnimationController controller;
//    ScrollSpringSimulation sim;
    
    @override
    void initState() {
        super.initState();
        boxPos = widget.boxPosition;
//        sim = ScrollSpringSimulation(
//            SpringDescription(
//                mass: 1.0,
//                stiffness: 1.0,
//                damping: 1.0,
//            ),
//            0.0,
//            1.0,
//            0.0);
//        controller = AnimationController(vsync: this)
//            ..addListener(() {
//                print("${sim.x(controller.value)}");
//            });
    }
    
    @override
    Widget build(BuildContext context) {
        return GestureDetector(
            onPanStart: startDrag,
            onPanUpdate: onDrag,
            onPanEnd: endDrag,
            child: CustomPaint(
                painter: BoxPainter(
                    boxPos: boxPos,
                    boxPosOnStart: boxPosOnStart ?? boxPos,
                    color: Colors.red,
                    touchPoint: point,
                ),
                child: Container(),
            ),
        );
    }
    
    void startDrag(DragStartDetails details) {
        start = (context.findRenderObject() as RenderBox).globalToLocal(details.globalPosition);
        boxPosOnStart = boxPos;
    }
    
    void onDrag(DragUpdateDetails details) {
        setState(() {
            point = (context.findRenderObject() as RenderBox).globalToLocal(details.globalPosition);
            final dragVec = start.dy - point.dy;
            final normDragVec = (dragVec / context.size.height).clamp(-1.0, 1.0);
            boxPos = (boxPosOnStart + normDragVec).clamp(0.0, 1.0);
        });
    }
    
    void endDrag(DragEndDetails details) {
        setState(() {
            start = null;
            point = null;
            boxPosOnStart = null;
        });
    }
}

class BoxPainter extends CustomPainter {
    final double boxPos;
    final double boxPosOnStart;
    final Color color;
    final Offset touchPoint;
    final Paint boxPaint;
    final Paint dropPaint;
    
    BoxPainter({
        this.boxPosOnStart = 0.0,
        this.boxPos = 0.0,
        this.color = Colors.grey,
        this.touchPoint,
    })
        : boxPaint = Paint(),
            dropPaint = Paint() //
    {
        boxPaint.color = this.color;
        boxPaint.style = PaintingStyle.fill;
        dropPaint.color = this.color;
        dropPaint.style = PaintingStyle.fill;
    }
    
    @override
    void paint(Canvas canvas, Size size) {
        canvas.clipRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height));
        final boxValueY = size.height - (size.height * boxPos);
        final prevBoxValueY = size.height - (size.height * boxPosOnStart);
        final midPointY = ((boxValueY - prevBoxValueY) * 1.2 + prevBoxValueY).clamp(0.0, size.height);
        Point left, mid, right;
        left = Point(-100.0, prevBoxValueY);
        right = Point(size.width + 100.0, prevBoxValueY);
        if (null != touchPoint) {
            mid = Point(touchPoint.dx, midPointY);
        } else {
            mid = Point(size.width / 2, midPointY);
        }
        final path = Path();
        path.moveTo(mid.x, mid.y);
        path.quadraticBezierTo(mid.x - 100.0, mid.y, left.x, left.y);
        path.lineTo(0.0, size.height);
        path.moveTo(mid.x, mid.y);
        path.quadraticBezierTo(mid.x + 100.0, mid.y, right.x, right.y);
        path.lineTo(size.width, size.height);
        path.lineTo(0.0, size.height);
        path.close();
        canvas.drawPath(path, boxPaint);
        canvas.drawCircle(Offset(right.x, right.y), 10.0, dropPaint);
    }
    
    @override
    bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
    }
}

class TouchDemo2 extends StatefulWidget {
    @override
    State<StatefulWidget> createState() {
        return TouchDemo2State();
    }
}

class TouchDemo2State extends State<TouchDemo2> {
    Color caughtColor = Colors.grey;
    
    @override
    Widget build(BuildContext context) {
        return Stack(
            children: <Widget>[
                DragBox(
                    initPos: Offset(10.0, 10.0),
                    label: "111",
                    itemColor: Colors.deepPurple,
                ),
                DragBox(
                    initPos: Offset(200.0, 10.0),
                    label: "222",
                    itemColor: Colors.deepOrange,
                ),
                Positioned(
                    left: 100.0,
                    bottom: 0,
                    child: DragTarget(onAccept: (Color color) {
                        caughtColor = color;
                    }, builder: (BuildContext ctx, List<dynamic> accepted, List<dynamic> rejected) {
                        return Container(
                            width: 200,
                            height: 200,
                            decoration: BoxDecoration(
                                color: accepted.isEmpty ? caughtColor : Colors.grey.shade200,
                            ),
                            child: Text("Drag Here"),
                        );
                    }),
                ),
            ],
        );
    }
}

class DragBox extends StatefulWidget {
    final Offset initPos;
    final String label;
    final Color itemColor;
    
    DragBox({this.initPos, this.label, this.itemColor});
    
    @override
    State<StatefulWidget> createState() {
        return DragBoxState();
    }
}

class DragBoxState extends State<DragBox> {
    Offset position = Offset.zero;
    
    @override
    void initState() {
        super.initState();
        position = widget.initPos;
    }
    
    @override
    Widget build(BuildContext context) {
        return Positioned(
            left: position.dx,
            top: position.dy,
            child: Draggable(
                data: widget.itemColor,
                child: Container(
                    width: 100.0,
                    height: 100.0,
                    color: widget.itemColor,
                    child: Center(
                        child: Text(
                            widget.label,
                            style: TextStyle(color: Colors.white, fontSize: 20.0),
                        ),
                    ),
                ),
                feedback: Container(
                    width: 110.0,
                    height: 110.0,
                    color: widget.itemColor.withOpacity(0.5),
                    child: Material(
                        color: Colors.transparent,
                        child: Center(
                            child: Text(
                                widget.label,
                                style: TextStyle(color: Colors.white, fontSize: 20.0),
                            ),
                        ),
                    ),
                ),
                onDraggableCanceled: (v, o) {
                    setState(() {
                        position = o;
                    });
                },
            ),
        );
    }
}
